Province Report

Western Cape (WC) — Provincial surveillance report: notification counts, incidence rates, epidemic curves, and condition breakdowns.

Data through: 19 February 2026  |  Population: 7,187,004

Home All Provinces

Total Cases

1,519

%B %Y

Month Δ

↓ 35.1%%

vs previous month

Conditions

26

reporting this month

Top Condition

Tuberculosis:pulmonary

highest case count


Filter

Use these controls to filter the monthly charts and data table below.

div(
  style = "display:flex; gap:2rem; flex-wrap:wrap; margin-bottom:1.5rem;",
  div(style = "min-width:260px;", filter_select("cond", "Condition", sd_monthly, ~Condition)),
  div(style = "min-width:180px;", filter_slider("cases", "Cases \u2265", sd_monthly, ~cases, min = 0))
)

Epidemic Curves

Weekly Cases

plot_ly(weekly, x = ~Week, y = ~cases, color = ~Condition,
        type = "bar",
        hoverinfo = "text",
        text = ~paste0(Condition, "<br>Week: ", format(Week, "%d %b %Y"),
                       "<br>Cases: ", cases)) %>%
  layout(barmode = "stack",
         xaxis = list(title = "Week starting"),
         yaxis = list(title = "Weekly cases"),
         legend = list(orientation = "h", y = -0.15))
Warning in RColorBrewer::brewer.pal(max(N, 3L), "Set2"): n too large, allowed maximum for palette Set2 is 8
Returning the palette you asked for with that many colors
Warning in RColorBrewer::brewer.pal(max(N, 3L), "Set2"): n too large, allowed maximum for palette Set2 is 8
Returning the palette you asked for with that many colors
Figure 1: Weekly case counts in Western Cape stacked by condition

Monthly Cases

plot_ly(sd_monthly, x = ~Month, y = ~cases, color = ~Condition,
        type = "bar",
        hoverinfo = "text",
        text = ~paste0(Condition, "<br>", format(Month, "%b %Y"),
                       "<br>Cases: ", cases)) %>%
  layout(barmode = "stack",
         xaxis = list(title = "Month"),
         yaxis = list(title = "Monthly cases"),
         legend = list(orientation = "h", y = -0.15))
Warning in RColorBrewer::brewer.pal(max(N, 3L), "Set2"): n too large, allowed maximum for palette Set2 is 8
Returning the palette you asked for with that many colors
Warning in RColorBrewer::brewer.pal(max(N, 3L), "Set2"): n too large, allowed maximum for palette Set2 is 8
Returning the palette you asked for with that many colors
Figure 2: Monthly case counts in Western Cape — linked to filter above

Monthly Incidence

plot_ly(sd_monthly, x = ~Month, y = ~incidence_100k, color = ~Condition,
        type = "scatter", mode = "lines+markers",
        marker = list(size = 6, opacity = 0.8),
        hoverinfo = "text",
        text = ~paste0(Condition, "<br>", format(Month, "%b %Y"),
                       "<br>Inc: ", incidence_100k, " /100k")) %>%
  layout(xaxis = list(title = "Month"),
         yaxis = list(title = "Incidence per 100 000"),
         legend = list(orientation = "h", y = -0.15))
Warning in RColorBrewer::brewer.pal(max(N, 3L), "Set2"): n too large, allowed maximum for palette Set2 is 8
Returning the palette you asked for with that many colors
Warning in RColorBrewer::brewer.pal(max(N, 3L), "Set2"): n too large, allowed maximum for palette Set2 is 8
Returning the palette you asked for with that many colors
Figure 4: Monthly incidence per 100 000 in Western Cape — linked to filter above

Data Tables

Annual Counts

DT::datatable(
  annual_wide,
  caption = paste0("Annual notification counts — ", prov_name),
  extensions = "Buttons",
  filter   = "top",
  rownames = FALSE,
  options  = list(
    dom        = "Bfrtip",
    buttons    = c("csv", "excel", "pdf"),
    pageLength = 25,
    scrollX    = TRUE,
    order      = list(list(ncol(annual_wide) - 1, "desc"))
  )
)
Table 1

Annual Incidence

DT::datatable(
  inci_wide,
  caption = paste0("Annual incidence per 100 000 — ", prov_name,
                   " (pop: ", format(prov_pop, big.mark = ","), ")"),
  extensions = "Buttons",
  filter   = "top",
  rownames = FALSE,
  options  = list(
    dom        = "Bfrtip",
    buttons    = c("csv", "excel", "pdf"),
    pageLength = 25,
    scrollX    = TRUE
  )
)
Table 2

Monthly Detail

DT::datatable(
  sd_monthly,
  caption = paste0("Monthly data — ", prov_name, " (linked to filter above)"),
  extensions = "Buttons",
  filter   = "top",
  rownames = FALSE,
  options  = list(
    dom        = "Bfrtip",
    buttons    = c("csv", "excel"),
    pageLength = 20,
    scrollX    = TRUE
  )
)
Table 3

Province Navigation


TipRelated Sections